package mxp

import java.nio.file.Files
import java.nio.file.Paths
import kotlin.math.max
import kotlin.system.exitProcess

fun process_macros_list(model: MCodeDefModel, steps: List<String>): List<String>? {
    var more = false
    val new_steps = mutableListOf<String>()

    steps.forEach { step ->
        val signals = step.split('|').filter { it.startsWith("$") }
        if (signals.size == 1) {
            more = true

            val key = signals[0].substring(1)
            when {
                model.single_macros.containsKey(key) -> new_steps.add(
                    step.replace(
                        signals[0],
                        model.single_macros[key] as String
                    )
                )
                model.list_macros.containsKey(key) -> new_steps.addAll(model.list_macros[key] as List<String>)
                else -> {
                    println("未知宏: $key")
                    exitProcess(1)
                }
            }
        } else if (signals.size > 1) {
            println("同一行不允许出现多个宏：$step")
            exitProcess(1)
        } else {
            new_steps.add(step)
        }
    }

    if (more) {
        return new_steps
    } else {
        return null
    }
}

fun process_macros(model: MCodeDefModel) {
    while (true) {
        var more = false
        val new_list_macros = mutableMapOf<String, MutableList<String>>()

        model.list_macros.forEach { name, steps ->
            val new_steps = mutableListOf<String>()
            steps.forEach { step ->
                val signals = step.split('|').filter { it.startsWith("$") }
                if (signals.size == 1) {
                    more = true

                    val key = signals[0].substring(1)
                    when {
                        model.single_macros.containsKey(key) -> new_steps.add(
                            step.replace(
                                signals[0],
                                model.single_macros[key] as String
                            )
                        )
                        model.list_macros.containsKey(key) -> new_steps.addAll(model.list_macros[key] as List<String>)
                        else -> {
                            println("未知宏: $key")
                            exitProcess(1)
                        }
                    }
                } else if (signals.size > 1) {
                    println("同一行不允许出现多个宏：$step")
                    exitProcess(1)
                } else {
                    new_steps.add(step)
                }
            }
            new_list_macros[name] = new_steps
        }

        model.list_macros = new_list_macros

        if (!more) {
            break
        }
    }

    model.instructions.forEach { instruction ->
        while (true) {
            var more = false

            instruction.am?.forEach { am, steps ->
                val new_steps = process_macros_list(model, steps)
                if (new_steps != null) {
                    instruction.am[am] = new_steps
                    more = true
                }
            }

            instruction.`when`?.forEach { cond, ams ->
                ams.forEach { am, steps ->
                    val new_steps = process_macros_list(model, steps)
                    if (new_steps != null) {
                        ams[am] = new_steps
                        more = true
                    }
                }
            }

            if (!more) {
                break
            }
        }
    }
}

fun parseSignals(model: MCodeDefModel, signals: String): Int {
    var value = 0

    signals.split('|').forEach { signal ->
        val pos = model.signals.indexOf(signal)
        value += 1 shl pos
    }

    return value
}

fun parseAm(model: MCodeDefModel, am_name: String): Int {
    for (i in 0 until model.ams.size) {
        if (am_name == model.ams[i]) {
            return i
        }
    }
    println("未知寻址模式: $am_name")
    exitProcess(1)
}

fun calcCond(model: MCodeDefModel, cond: String, value: Int): Boolean {
    val items = cond.split("==")
    val flag_bit = model.flags.indexOf(items[0])
    val flag_value = Integer.parseInt(items[1])
    val cond_value = flag_value shl flag_bit
    return (value and flag_bit) == cond_value
}

fun flagBits2Cond(model: MCodeDefModel, value: Int): List<String> {
    val result = mutableListOf<String>()

    for (i in 0 until model.flags.size) {
        result.add("${model.flags[i]}==${if ((i and value) > 0) 1 else 0}")
    }

    return result
}

fun calcROM(model: MCodeDefModel): Array<Int> {
    val rom = Array(65536) { 0 }

    val fetchSteps = model.instructions.find { it.name == "fetch" }!!.am["default"]!!.size

    model.instructions.forEach { instruction ->
        val offset = if (instruction.name == "fetch") 0 else fetchSteps

        instruction.am?.forEach { am_name, steps ->
            val am = parseAm(model, am_name)
            val opcode = (str2int(instruction.code) shl 3) + am
            for (i in 0 until steps.size) {
                val signals = steps[i]
                val mc = i + offset
                for (j in 0 until (1 shl model.flags.size)) {
                    val addr = (opcode shl 8) + (mc shl 2) + j
                    rom[addr] = parseSignals(model, signals)
                }
            }
        }

        instruction.`when`?.forEach { cond, ams ->
            val flagIndex = model.flags.indexOf(cond.split("==")[0])
            val flagValue = str2int(cond.split("==")[1])

            ams.forEach { am_name, steps ->
                val am = parseAm(model, am_name)
                val opcode = (str2int(instruction.code) shl 3) + am

                for (i in 0 until steps.size) {
                    val signals = steps[i]
                    val mc = i + offset
                    for (j in (0 until (2 shl model.flags.size))) {
                        if ((j shr flagIndex) and 0x01 == flagValue) {
                            val addr = (opcode shl 8) + (mc shl 2) + j
                            rom[addr] = parseSignals(model, signals)
                        }
                    }
                }
            }
        }
    }

    return rom
}

fun convert(input: String): String {
    val model = MCodeDefModel.fromJson(input)

    process_macros(model)

    model.list_macros.forEach { name, steps ->
        println("$name\t:\t$steps")
    }


    model.instructions.forEach { instruction ->
        println(instruction.name)
        instruction.am?.forEach { type, steps ->
            println("\t[${steps.size}] $type:\t$steps")
        }
        instruction.`when`?.forEach { cond, ams ->
            println("\t$cond")
            ams.forEach { type, steps ->
                println("\t\t[${steps.size}] $type:\t$steps")
            }
        }
    }


    val rom = calcROM(model)

    return rom2logisim(rom)
}

fun main() {
    val src = "../mcode.json"
    val dst = "../mcode.bin"

    val input = Files.readAllLines(Paths.get(src), charset("UTF-8")).map { it.replace("//.*$","")}.joinToString("\n")
    val output = convert(input)
    Files.write(Paths.get(dst), output.toByteArray(charset("UTF-8")))
}